package com.lt.integration.jpa.component;

import com.lt.integration.jpa.annotation.QOAttr;
import com.lt.integration.jpa.annotation.QOAttrType;
import com.lt.integration.jpa.annotation.QOConfig;
import com.lt.integration.jpa.component.common.Direction;
import com.lt.integration.jpa.component.common.MatchMode;
import com.lt.integration.jpa.component.hibernate.Updater;
import org.hibernate.Criteria;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.persistence.EntityManager;
import javax.persistence.criteria.*;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @Auther: weidian
 * @Date: 2018/7/24 00:43
 * @Description:
 */
@Transactional
public class CommonDao<T extends BaseModel, QO extends BaseQO> extends SimpleJpaRepository<T, Long> {

	private static final Logger logger = LoggerFactory.getLogger(CommonDao.class);

	public static final String PRIMARY_ID = "id";
	public static final String UPDATE_STRING = "update %s x set";

	private final EntityManager entityManager; // 父类没有不带参数的构造方法，这里手动构造父类
	private final Class<T> domainClass;

	public CommonDao(Class<T> domainClass, EntityManager entityManager) {
		super(domainClass, entityManager);
		this.domainClass = domainClass;
		this.entityManager = entityManager;
	}

	public EntityManager getEntityManager() {
		return entityManager;
	}

	@Override
	protected Class<T> getDomainClass() {
		return domainClass;
	}
	
	/**
	 * 保存
	 * @param entity
	 * @return
	 */
	public T persist(T entity) {
		Assert.notNull(entity, "entity must not be null");
		Assert.notNull(entity.getId(), "id must not be null");
		entityManager.persist(entity);
		return entity;
	}

	protected <X, Q> CriteriaQuery<X> buildQueryWithoutOrderBy(CriteriaBuilder criteriaBuilder,
			CriteriaQuery<X> criteriaQuery, Root<Q> root, BaseQO qo) {
		criteriaQuery = buildQuery(criteriaBuilder, criteriaQuery, root, qo);
		criteriaQuery.orderBy(new ArrayList<>());
		return criteriaQuery;
	}

	protected <X, Q> CriteriaQuery<X> buildQuery(CriteriaBuilder criteriaBuilder, CriteriaQuery<X> criteriaQuery,
			Root<Q> root, BaseQO qo) {
		EntityType<Q> entityType = root.getModel();
		List<Predicate> predicatesList = new ArrayList<>();

		// ids
		List<Long> ids = qo.getIds();

		if (null != ids && ids.size() > 0) {// 通过ids查询
			CriteriaBuilder.In<Long> in = criteriaBuilder
					.in(root.get(entityType.getSingularAttribute("id", Long.class)));

			for (Long id : ids) {
				in.value(id);
			}
			predicatesList.add(in);
		}

		List<Long> excludeIds = qo.getExcludeIds();

		if (null != excludeIds && excludeIds.size() > 0) {// 通过ids查询

			Predicate[] predicates = new Predicate[excludeIds.size()];
			for (int i = 0; i < excludeIds.size(); i++) {
				predicates[i] = criteriaBuilder.notEqual(root.get(entityType.getSingularAttribute("id", Long.class)),
						excludeIds.get(i));
			}

			predicatesList.add(criteriaBuilder.and(predicates));
		}

		Map<String, List<Field>> fieldMap = QOUtils.getQoFieldMap(qo);
		Map<String, Integer> orderMap = null;

		for (Map.Entry<String, List<Field>> entry : fieldMap.entrySet()) {
			List<Predicate> predicates = new ArrayList<>();

			for (Field field : entry.getValue()) {
				QOAttr queryAttribute = field.getAnnotation(QOAttr.class);

				// 属性名称
				String propertyName = queryAttribute.name();
				Object fieldValue = null;
				try {
					fieldValue = field.get(qo);
				} catch (Exception e) {
				}

				if (fieldValue == null) {
					continue;
				}

				logger.debug("propertyName: {}, fieldValue: {}", propertyName, fieldValue);

				if (QOUtils.checkIfStringBlank(fieldValue)) {
					continue;
				}

				// like查询
				if (QOUtils.isTrue(qo, queryAttribute.ifTrueUseLike())) {
					
					predicates.add(
							criteriaBuilder.like(get(propertyName, root, String.class),
									MatchMode.ANYWHERE((String) fieldValue)));
					continue;
				}

				// 完全匹配
				if (QOAttrType.EQ.equals(queryAttribute.type())) {
					predicates.add(criteriaBuilder.equal(get(propertyName, root), fieldValue));
				}
				// 完全匹配或为NULL
				else if (QOAttrType.EQ_OR_NULL.equals(queryAttribute.type())) {
					predicates.add(criteriaBuilder.or(criteriaBuilder.equal(get(propertyName, root), fieldValue),
							criteriaBuilder.isNull(get(propertyName, root))));
				}
				// 不匹配
				else if (QOAttrType.NE.equals(queryAttribute.type())) {
					predicates.add(criteriaBuilder.notEqual(get(propertyName, root), fieldValue));
				}
				// 不匹配或为NULL
				else if (QOAttrType.NE_OR_NULL.equals(queryAttribute.type())) {
					predicates.add(criteriaBuilder.or(criteriaBuilder.equal(get(propertyName, root), fieldValue),
							criteriaBuilder.isNotNull(get(propertyName, root))));
				}
				// 大于等于
				else if (QOAttrType.GE.equals(queryAttribute.type())) {
					if (Date.class.isAssignableFrom(field.getType())) {
						predicates.add(criteriaBuilder.greaterThanOrEqualTo(get(propertyName, root, Date.class), (Date) fieldValue));
					} else if (Number.class.isAssignableFrom(field.getType())) {
                        buildNumberCompare(QOAttrType.GE, criteriaBuilder, root, predicates, field, propertyName, (Number) fieldValue);
                    } else {
						continue;
					}
				}
				// 大于
				else if (QOAttrType.GT.equals(queryAttribute.type())) {
					if (Date.class.isAssignableFrom(field.getType())) {
						predicates.add(criteriaBuilder.greaterThan(get(propertyName, root, Date.class), (Date) fieldValue));
					} else if (Number.class.isAssignableFrom(field.getType())) {
                        buildNumberCompare(QOAttrType.GT, criteriaBuilder, root, predicates, field, propertyName, (Number) fieldValue);
                    } else {
						continue;
					}
				}
				// 小于等于
				else if (QOAttrType.LE.equals(queryAttribute.type())) {
					if (Date.class.isAssignableFrom(field.getType())) {
						predicates.add(
								criteriaBuilder.lessThanOrEqualTo(get(propertyName, root, Date.class), (Date) fieldValue));
					} else if (Number.class.isAssignableFrom(field.getType())) {
                        buildNumberCompare(QOAttrType.LE, criteriaBuilder, root, predicates, field, propertyName, (Number) fieldValue);
                    } else {
						continue;
					}
				}
				// 小于
				else if (QOAttrType.LT.equals(queryAttribute.type())) {
					if (Date.class.isAssignableFrom(field.getType())) {
						predicates.add(criteriaBuilder.lessThan(get(propertyName, root, Date.class), (Date) fieldValue));
					} else if (Number.class.isAssignableFrom(field.getType())) {
                        buildNumberCompare(QOAttrType.LT, criteriaBuilder, (Root<Q>) root, predicates, field, propertyName, (Number) fieldValue);
                    } else {
						continue;
					}
				}
				// 模糊匹配任何位置
				else if (QOAttrType.LIKE_ANYWHERE.equals(queryAttribute.type())) {
					predicates.add(
							criteriaBuilder.like(get(propertyName, root, String.class),
									MatchMode.ANYWHERE((String) fieldValue)));
				}
				// 模糊匹配开始位置
				else if (QOAttrType.LIKE_START.equals(queryAttribute.type())) {
					predicates.add(
							criteriaBuilder.like(get(propertyName, root, String.class),
									MatchMode.START((String) fieldValue)));
				}
				// 模糊匹配结尾
				else if (QOAttrType.LIKE_END.equals(queryAttribute.type())) {
					predicates.add(
							criteriaBuilder.like(get(propertyName, root, String.class),
									MatchMode.END((String) fieldValue)));
				}
				// 包含
				else if (QOAttrType.IN.equals(queryAttribute.type())) {

					if (field.getType().isArray()) {
						Object[] objects = (Object[]) fieldValue;
						if (objects.length == 0) {
							continue;
						}

						CriteriaBuilder.In<Object> in = criteriaBuilder.in(get(propertyName, root));

						for (Object obj : objects) {
							in.value(obj);
						}

						predicates.add(in);
					} else {
						@SuppressWarnings("unchecked")
						Collection<Object> objects = (Collection<Object>) fieldValue;
						if (objects.size() == 0) {
							continue;
						}

						CriteriaBuilder.In<Object> in = criteriaBuilder.in(get(propertyName, root));

						for (Object obj : objects) {
							in.value(obj);
						}

						predicates.add(in);
					}
				}
				// 排序
				else if (QOAttrType.ORDER.equals(queryAttribute.type())) {
					Number num = (Number) fieldValue;
					if (num.intValue() == 0) {
						continue;
					}
					if (null == orderMap) {
						orderMap = new HashMap<>();
					}
					
					orderMap.put(propertyName, num.intValue());
				}
				// 立即加载
				else if (QOAttrType.FATCH_EAGER.equals(queryAttribute.type())) {
					if (fieldValue instanceof Boolean && ((Boolean) fieldValue)) {
						root.fetch(propertyName, JoinType.LEFT);
					} else {
						continue;
					}
				}
				// 内联表
				else if (QOAttrType.JOIN.equals(queryAttribute.type())) {

					QOConfig queryObject = field.getAnnotation(QOConfig.class);
					if (null != queryObject) {
						String configName = queryObject.name();
						predicates.add(criteriaBuilder.equal(root.join(propertyName, JoinType.INNER).get(configName),
								fieldValue));
					} else {
						if (fieldValue instanceof Boolean && ((Boolean) fieldValue)) {
							root.fetch(propertyName, JoinType.INNER);
						} else {
							continue;
						}
					}
				}
				// 左外联表
				else if (QOAttrType.LEFT_JOIN.equals(queryAttribute.type())) {
					QOConfig queryObject = field.getAnnotation(QOConfig.class);
					if (null != queryObject) {
						String configName = queryObject.name();
						predicates.add(criteriaBuilder.equal(root.join(propertyName, JoinType.LEFT).get(configName),
								fieldValue));
					} else {
						if (fieldValue instanceof Boolean && ((Boolean) fieldValue)) {
							root.fetch(propertyName, JoinType.LEFT);
						} else {
							continue;
						}
					}
				}
				// 右外联表
				else if (QOAttrType.RIGHT_JOIN.equals(queryAttribute.type())) {
					QOConfig queryObject = field.getAnnotation(QOConfig.class);
					if (null != queryObject) {
						String configName = queryObject.name();
						predicates.add(criteriaBuilder.equal(root.join(propertyName, JoinType.RIGHT).get(configName),
								fieldValue));
					} else {
						if (fieldValue instanceof Boolean && ((Boolean) fieldValue)) {
							root.fetch(propertyName, JoinType.RIGHT);
						} else {
							continue;
						}
					}
				}

			}

			// 组装条件
			if (predicates.size() > 0) {
				if (entry.getKey() == null) {
					for (Predicate predicate : predicates) {
						predicatesList.add(predicate);
					}
				} else {
					Predicate[] predicateArray = new Predicate[predicates.size()];
					predicateArray = predicates.toArray(predicateArray);
					predicatesList.add(criteriaBuilder.or(predicateArray));
				}
			}
		}
		
		// 处理排序
		if (null != orderMap && !orderMap.isEmpty()) {
			if (orderMap.size() == 1) {
				// 只有一个元素时 不需要做排序
				for (Map.Entry<String, Integer> entry : orderMap.entrySet()) {
					criteriaQuery.orderBy(entry.getValue() > 0
							? criteriaBuilder.asc(get(entry.getKey(), root))
							: criteriaBuilder.desc(get(entry.getKey(), root)));
				}
			} else {
				List<Map.Entry<String, Integer>> list = new ArrayList<>();
				for (Map.Entry<String, Integer> entry : orderMap.entrySet()) {
					list.add(entry);
				}
				list.sort((m1, m2) -> Math.abs(m1.getValue().intValue()) - Math.abs(m2.getValue()));
				
				for (Map.Entry<String, Integer> entry : list) {
					criteriaQuery.orderBy(entry.getValue() > 0
							? criteriaBuilder.asc(get(entry.getKey(), root))
							: criteriaBuilder.desc(get(entry.getKey(), root)));
				}
			}
		}

		// 处理id cursor
		if (qo.getCursor() != null) {
			Long id = qo.getCursor();
			Direction direction = qo.getDirection();
			if (null == direction) {
				direction = Direction.ASC;
			}

			// 直接覆盖上面的排序
			List<Order> orderList = new ArrayList<>();
			Predicate predicate = null;
			if (direction == Direction.ASC) {
				orderList.add(criteriaBuilder.asc(root.get(CommonDao.PRIMARY_ID)));
				predicate = criteriaBuilder.gt(root.get(CommonDao.PRIMARY_ID), id);
			} else {
				orderList.add(criteriaBuilder.desc(root.get(CommonDao.PRIMARY_ID)));
				predicate = criteriaBuilder.lt(root.get(CommonDao.PRIMARY_ID), id);
			}
			criteriaQuery.orderBy(orderList);
			predicatesList.add(predicate);
		}

		criteriaQuery.where(predicatesList.toArray(new Predicate[predicatesList.size()]));
		return criteriaQuery;
	}

    private <Q> void buildNumberCompare(QOAttrType type, CriteriaBuilder criteriaBuilder, Root<Q> root, List<Predicate> predicates, Field field, String propertyName, Number fieldValue) {
		if (QOAttrType.GE.equals(type)) {
			if (field.getType().equals(Integer.class)) {
				predicates.add(criteriaBuilder.ge(get(propertyName, root, Integer.class), fieldValue));
			} else if (field.getType().equals(Long.class)) {
				predicates.add(criteriaBuilder.ge(get(propertyName, root, Long.class), fieldValue));
			} else if (field.getType().equals(Double.class)) {
				predicates.add(criteriaBuilder.ge(get(propertyName, root, Double.class), fieldValue));
			} else if (field.getType().equals(Float.class)) {
				predicates.add(criteriaBuilder.ge(get(propertyName, root, Float.class), fieldValue));
			} else {
			}
		}
		// 大于
		else if (QOAttrType.GT.equals(type)) {
			if (field.getType().equals(Integer.class)) {
				predicates.add(criteriaBuilder.gt(get(propertyName, root, Integer.class), fieldValue));
			} else if (field.getType().equals(Long.class)) {
				predicates.add(criteriaBuilder.gt(get(propertyName, root, Long.class), fieldValue));
			} else if (field.getType().equals(Double.class)) {
				predicates.add(criteriaBuilder.gt(get(propertyName, root, Double.class), fieldValue));
			} else if (field.getType().equals(Float.class)) {
				predicates.add(criteriaBuilder.gt(get(propertyName, root, Float.class), fieldValue));
			} else {
			}
		}
		// 小于等于
		else if (QOAttrType.LE.equals(type)) {
			if (field.getType().equals(Integer.class)) {
				predicates.add(criteriaBuilder.le(get(propertyName, root, Integer.class), fieldValue));
			} else if (field.getType().equals(Long.class)) {
				predicates.add(criteriaBuilder.le(get(propertyName, root, Long.class), fieldValue));
			} else if (field.getType().equals(Double.class)) {
				predicates.add(criteriaBuilder.le(get(propertyName, root, Double.class), fieldValue));
			} else if (field.getType().equals(Float.class)) {
				predicates.add(criteriaBuilder.le(get(propertyName, root, Float.class), fieldValue));
			} else {
			}
		}
		// 小于
		else if (QOAttrType.LT.equals(type)) {

			if (field.getType().equals(Integer.class)) {
				predicates.add(criteriaBuilder.lt(get(propertyName, root, Integer.class), fieldValue));
			} else if (field.getType().equals(Long.class)) {
				predicates.add(criteriaBuilder.lt(get(propertyName, root, Long.class), fieldValue));
			} else if (field.getType().equals(Double.class)) {
				predicates.add(criteriaBuilder.lt(get(propertyName, root, Double.class), fieldValue));
			} else if (field.getType().equals(Float.class)) {
				predicates.add(criteriaBuilder.lt(get(propertyName, root, Float.class), fieldValue));
			} else {
			}
		}

    }

    protected Criteria buildCriteria(Criteria criteria, BaseQO qo) {

		Long id = qo.getId();

		if (null != id) {// 如果id不为空 直接通过id查询
			criteria.add(Restrictions.eq("id", id));
		}

		// ids
		List<Long> ids = qo.getIds();

		if (null != ids && ids.size() > 0) {// 通过ids查询
			criteria.add(Restrictions.in("id", ids.toArray(new Object[ids.size()])));
		}

		List<Long> excludeIds = qo.getExcludeIds();

		if (null != excludeIds && excludeIds.size() > 0) {// 通过ids查询

			Criterion[] criterions = new Criterion[excludeIds.size()];
			for (int i = 0; i < excludeIds.size(); i++) {
				criterions[i] = Restrictions.ne("id", excludeIds.get(i));
			}

			criteria.add(Restrictions.and(criterions));
		}

		Map<String, List<Field>> fieldMap = QOUtils.getQoFieldMap(qo);
		Map<String, Integer> orderMap = null;

		for (Map.Entry<String, List<Field>> entry : fieldMap.entrySet()) {

			List<Criterion> criterions = new ArrayList<>();

			for (Field field : entry.getValue()) {
				QOAttr queryAttribute = field.getAnnotation(QOAttr.class);

				String propertyName = queryAttribute.name();
				Object fieldValue = null;
				try {
					fieldValue = field.get(qo);
				} catch (Exception e) {
				}

				if (fieldValue == null) {
					continue;
				}
				if (QOUtils.checkIfStringBlank(fieldValue)) {
					continue;
				}

				// like查询
				if (QOUtils.isTrue(qo, queryAttribute.ifTrueUseLike())) {
					criterions.add(Restrictions.like(propertyName, (String) fieldValue,
							org.hibernate.criterion.MatchMode.ANYWHERE));
					continue;
				}

				// 完全匹配
				if (QOAttrType.EQ.equals(queryAttribute.type())) {
					criterions.add(Restrictions.eq(propertyName, fieldValue));
				}
				// 完全匹配或为NULL
				else if (QOAttrType.EQ_OR_NULL.equals(queryAttribute.type())) {
					criterions.add(Restrictions.eqOrIsNull(propertyName, fieldValue));
				}
				// 不匹配
				else if (QOAttrType.NE.equals(queryAttribute.type())) {
					criterions.add(Restrictions.ne(propertyName, fieldValue));
				}
				// 不匹配或为NULL
				else if (QOAttrType.NE_OR_NULL.equals(queryAttribute.type())) {
					criterions.add(Restrictions.neOrIsNotNull(propertyName, fieldValue));
				}
				// 大于等于
				else if (QOAttrType.GE.equals(queryAttribute.type())) {
					criterions.add(Restrictions.ge(propertyName, fieldValue));
				}
				// 大于
				else if (QOAttrType.GT.equals(queryAttribute.type())) {
					criterions.add(Restrictions.gt(propertyName, fieldValue));
				}
				// 小于等于
				else if (QOAttrType.LE.equals(queryAttribute.type())) {
					criterions.add(Restrictions.le(propertyName, fieldValue));
				}
				// 小于
				else if (QOAttrType.LT.equals(queryAttribute.type())) {
					criterions.add(Restrictions.lt(propertyName, fieldValue));
				}
				// 模糊匹配任何位置
				else if (QOAttrType.LIKE_ANYWHERE.equals(queryAttribute.type())) {
					criterions.add(Restrictions.like(propertyName, (String) fieldValue,
							org.hibernate.criterion.MatchMode.ANYWHERE));
				}
				// 模糊匹配开始位置
				else if (QOAttrType.LIKE_START.equals(queryAttribute.type())) {
					criterions.add(Restrictions.like(propertyName, (String) fieldValue,
							org.hibernate.criterion.MatchMode.START));
				}
				// 模糊匹配结尾
				else if (QOAttrType.LIKE_END.equals(queryAttribute.type())) {
					criterions.add(Restrictions.like(propertyName, (String) fieldValue,
							org.hibernate.criterion.MatchMode.END));
				}
				// 包含
				else if (QOAttrType.IN.equals(queryAttribute.type())) {
					if (field.getType().isArray()) {
						Object[] objects = (Object[]) fieldValue;
						if (objects.length == 0) {
							continue;
						}

						criterions.add(Restrictions.in(propertyName, objects));
					} else {
						@SuppressWarnings("unchecked")
						Collection<Object> objects = (Collection<Object>) fieldValue;
						if (objects.size() == 0) {
							continue;
						}

						criterions.add(Restrictions.in(propertyName, objects));
					}
				}
				// 排序
				else if (QOAttrType.ORDER.equals(queryAttribute.type())) {
					Number num = (Number) fieldValue;
					if (num.intValue() == 0) {
						continue;
					}
					
					if (null == orderMap) {
						orderMap = new HashMap<>();
					}
					
					orderMap.put(propertyName, num.intValue());
				}
				// 立即加载
				else if (QOAttrType.FATCH_EAGER.equals(queryAttribute.type())) {
					if (fieldValue instanceof Boolean && ((Boolean) fieldValue)) {
						criteria.setFetchMode(propertyName, org.hibernate.FetchMode.JOIN);
					} else {
						continue;
					}
				}
				// 内联表
				else if (QOAttrType.JOIN.equals(queryAttribute.type())) {
					if (fieldValue instanceof Boolean && ((Boolean) fieldValue)) {
						throw new RuntimeException("查询部分属性时，不能使用关联查询");
					} else {
						continue;
					}
				}
				// 左外联表
				else if (QOAttrType.LEFT_JOIN.equals(queryAttribute.type())) {
					if (fieldValue instanceof Boolean && ((Boolean) fieldValue)) {
						throw new RuntimeException("查询部分属性时，不能使用关联查询");
					} else {
						continue;
					}
				}
				// 右外联表
				else if (QOAttrType.RIGHT_JOIN.equals(queryAttribute.type())) {
					if (fieldValue instanceof Boolean && ((Boolean) fieldValue)) {
						throw new RuntimeException("查询部分属性时，不能使用关联查询");
					} else {
						continue;
					}
				}
			}

			// 组装条件
			if (criterions.size() > 0) {
				if (entry.getKey() == null) {
					for (Criterion criterion : criterions) {
						criteria.add(criterion);
					}
				} else {
					Criterion[] criterionsArray = new Criterion[criterions.size()];
					criterions.toArray(criterionsArray);
					criteria.add(Restrictions.or(criterionsArray));
				}
			}
		}
		
		// 处理排序
		if (null != orderMap && !orderMap.isEmpty()) {
			if (orderMap.size() == 1) {
				// 只有一个元素时 不需要做排序
				for (Map.Entry<String, Integer> entry : orderMap.entrySet()) {
					criteria.addOrder(entry.getValue() > 0 ? org.hibernate.criterion.Order.asc(entry.getKey())
							: org.hibernate.criterion.Order.desc(entry.getKey()));
				}
			} else {
				List<Map.Entry<String, Integer>> list = new ArrayList<>();
				for (Map.Entry<String, Integer> entry : orderMap.entrySet()) {
					list.add(entry);
				}
				list.sort((m1, m2) -> Math.abs(m1.getValue().intValue()) - Math.abs(m2.getValue()));
				
				for (Map.Entry<String, Integer> entry : list) {
					criteria.addOrder(entry.getValue() > 0 ? org.hibernate.criterion.Order.asc(entry.getKey())
					: org.hibernate.criterion.Order.desc(entry.getKey()));
				}
			}
		}

		// 处理id cursor
		if (qo.getCursor() != null) {
			Long cursor = qo.getCursor();
			Direction direction = qo.getDirection();
			if (null == direction) {
				direction = Direction.ASC;
			}

			if (direction == Direction.ASC) {
				criteria.addOrder(org.hibernate.criterion.Order.asc(CommonDao.PRIMARY_ID));
				criteria.add(Restrictions.gt(CommonDao.PRIMARY_ID, cursor));
			} else {
				criteria.addOrder(org.hibernate.criterion.Order.desc(CommonDao.PRIMARY_ID));
				criteria.add(Restrictions.lt(CommonDao.PRIMARY_ID, cursor));
			}
		}

		return criteria;
	}

	/**
	 * 通过Updater更新对象
	 * 
	 * @param updater
	 * @return
	 */
	@Transactional(readOnly = false)
	public int updateByUpdater(Updater updater) {
		Map<Attribute<?, ?>, Object> condition = updater.getCondition();
		Assert.isTrue(null != condition && !condition.isEmpty(), "condition must not be null!");

		CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
		CriteriaUpdate<T> criteriaUpdate = criteriaBuilder.createCriteriaUpdate(domainClass);
		Root<T> root = criteriaUpdate.from(getDomainClass());

		Map<Attribute<?, ?>, Object> include = updater.getIncludeProperties();

		for (Map.Entry<Attribute<?, ?>, Object> map : include.entrySet()) {
			criteriaUpdate.set(map.getKey().getName(), map.getValue());
		}

		if (condition.size() > 0) {
			List<Predicate> predicatesList = new ArrayList<>();
			for (Map.Entry<Attribute<?, ?>, Object> map : condition.entrySet()) {
				predicatesList.add(criteriaBuilder.equal(root.get(map.getKey().getName()), map.getValue()));
			}
			criteriaUpdate.where(predicatesList.toArray(new Predicate[predicatesList.size()]));
		}

		return getEntityManager().createQuery(criteriaUpdate).executeUpdate();
	}
	
	private <Q> Expression<?> get(String propertyName, Root<Q> root){
		if (propertyName.indexOf(".") > 0) {
			String[] arr = propertyName.split("\\.");
			Path<?> path = null;
			for (String str : arr) {
				if (null == path) {
					path = root.get(str);	
				} else {
					path = path.get(str);
				}
			}
			return path;
		} else {
			return root.get(propertyName);
		}
	}
	
	private <Q, P> Expression<P> get(String propertyName, Root<Q> root, Class<P> clazz){
		if (propertyName.indexOf(".") > 0) {
			String[] arr = propertyName.split("\\.");
			
			Path<?> path = null;
			
			for (int i = 0; i < arr.length; i++) {
				if (null == path) {
					path = root.get(arr[i]);	
				} else {
					path = path.get(arr[i]);
				}
			}
			return path.as(clazz);
		} else {
			return root.get(root.getModel().getSingularAttribute(propertyName, clazz));
		}
	}


}
